package cn.turboinfo.fuyang.api.provider.common.service.impl.user;

import cn.turboinfo.fuyang.api.domain.common.service.user.SysUserService;
import cn.turboinfo.fuyang.api.domain.common.service.user.UserCredentialService;
import cn.turboinfo.fuyang.api.domain.common.service.user.UserLoginService;
import cn.turboinfo.fuyang.api.domain.common.service.user.UserProfileService;
import cn.turboinfo.fuyang.api.entity.admin.enumeration.login.AdminLoginOperation;
import cn.turboinfo.fuyang.api.entity.common.enumeration.user.LoginCheckType;
import cn.turboinfo.fuyang.api.entity.common.enumeration.user.LoginNameType;
import cn.turboinfo.fuyang.api.entity.common.exception.user.SysUserException;
import cn.turboinfo.fuyang.api.entity.common.pojo.user.*;
import cn.turboinfo.fuyang.api.provider.admin.framework.shiro.helper.PasswordHelper;
import cn.turboinfo.fuyang.api.provider.common.repository.database.role.RoleDAO;
import cn.turboinfo.fuyang.api.provider.common.repository.database.role.RolePO;
import cn.turboinfo.fuyang.api.provider.common.repository.database.user.SysUserDAO;
import cn.turboinfo.fuyang.api.provider.common.repository.database.user.SysUserPO;
import cn.turboinfo.fuyang.api.provider.common.repository.database.user.SysUserRoleDAO;
import cn.turboinfo.fuyang.api.provider.common.repository.database.user.SysUserRolePO;
import lombok.RequiredArgsConstructor;
import lombok.val;
import net.sunshow.toolkit.core.base.enums.EnableStatus;
import net.sunshow.toolkit.core.qbean.api.request.QPage;
import net.sunshow.toolkit.core.qbean.api.request.QRequest;
import net.sunshow.toolkit.core.qbean.api.response.QResponse;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanCreatorHelper;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanUpdaterHelper;
import net.sunshow.toolkit.core.qbean.helper.service.impl.DefaultQServiceImpl;
import nxcloud.foundation.core.data.support.annotation.EnableSoftDelete;
import nxcloud.foundation.core.idgenerator.IdGeneratorFacade;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Service
@EnableSoftDelete
public class SysUserServiceImpl extends DefaultQServiceImpl<SysUser, Long, SysUserPO, SysUserDAO> implements SysUserService {

    private final PasswordHelper passwordHelper;

    private final RoleDAO roleDAO;

    private final SysUserRoleDAO sysUserRoleDAO;

    private final UserLoginService userLoginService;

    private final UserCredentialService userCredentialService;

    private UserProfileService userProfileService;

    private final IdGeneratorFacade<Long> idGeneratorFacade;

    @Override
    public Optional<SysUser> getByUsername(String username) {
        return dao.findByUsername(username).map(this::convertQBean);
    }

    @Override
    public Optional<SysUser> getByMobile(String mobile) {
        return dao.findByMobile(mobile).map(this::convertQBean);
    }

    @Override
    @Transactional
    public SysUser save(SysUserCreator creator, String password) throws SysUserException {
        return save(creator, password, 0L);
    }

    @Override
    @Transactional
    public SysUser save(SysUserCreator creator, String password, Long agenciesId) throws SysUserException {
        // 如果设置了用户名 检测用户名是否重复
        if (StringUtils.isNotEmpty(creator.getUsername())) {
            if (dao.findByUsername(creator.getUsername()).isPresent()) {
                throw getExceptionSupplier("用户名已存在", null).get();
            }
        }

        // 如果设置了手机号 检测手机号是否重复
        if (StringUtils.isNotEmpty(creator.getMobile())) {
            if (dao.findByMobile(creator.getMobile()).isPresent()) {
                throw getExceptionSupplier("手机号已存在", null).get();
            }
        }

        SysUserPO sysUserPO = new SysUserPO();

        QBeanCreatorHelper.copyCreatorField(sysUserPO, creator);

        // 手动生成ID
        sysUserPO.setId(idGeneratorFacade.nextId());

        // 保存系统用户信息
        sysUserPO = dao.save(sysUserPO);

        // 保存凭证
        UserCredential userCredential = null;
        if (password != null) {
            String salt = passwordHelper.getSalt();
            String credential = passwordHelper.getCredential(password, salt);
            UserCredentialCreator.Builder userCredentialBuilder = UserCredentialCreator.builder()
                    .withUserId(sysUserPO.getId())
                    .withCredential(credential)
                    .withSalt(salt);
            userCredential = userCredentialService.save(userCredentialBuilder.build());
        }

        // 保存 user login
        if (StringUtils.isNotEmpty(sysUserPO.getUsername())) {
            if (userCredential != null) {
                UserLoginCreator.Builder userLoginBuilder = UserLoginCreator.builder()
                        .withUserId(sysUserPO.getId())
                        .withLoginName(sysUserPO.getUsername())
                        .withLoginNameType(LoginNameType.USERNAME)
                        .withLoginCheckType(LoginCheckType.ADMIN_CREDENTIAL)
                        .withLoginCheckId(userCredential.getId());
                userLoginService.save(userLoginBuilder.build());
            }
        }
        // 如果设置了手机号
        if (StringUtils.isNotEmpty(sysUserPO.getMobile())) {
            if (userCredential != null) {
                UserLoginCreator.Builder userLoginBuilder = UserLoginCreator.builder()
                        .withUserId(sysUserPO.getId())
                        .withLoginName(sysUserPO.getMobile())
                        .withLoginNameType(LoginNameType.PHONE_NUM)
                        .withLoginCheckType(LoginCheckType.ADMIN_CREDENTIAL)
                        .withLoginCheckId(userCredential.getId());
                userLoginService.save(userLoginBuilder.build());
            }
            {
                UserLoginCreator.Builder userLoginBuilder = UserLoginCreator.builder()
                        .withUserId(sysUserPO.getId())
                        .withLoginName(sysUserPO.getMobile())
                        .withLoginNameType(LoginNameType.PHONE_NUM)
                        .withLoginCheckType(LoginCheckType.ADMIN_VERIFY_CODE)
                        .withLoginCheckId(0L);
                userLoginService.save(userLoginBuilder.build());
            }
        }

        // 同步生成 user profile
        userProfileService.save(UserProfileCreator
                .builder()
                .withId(sysUserPO.getId())
                .withAgenciesId(agenciesId)
                .build());

        return convertQBean(sysUserPO);
    }

    @Override
    @Transactional
    public SysUser update(SysUserUpdater updater) throws SysUserException {
        return update(updater, 0L);
    }

    @Override
    @Transactional
    public SysUser update(SysUserUpdater updater, Long agenciesId) throws SysUserException {
        SysUserPO sysUserPO = getEntityWithNullCheckForUpdate(updater.getUpdateId(), dao);

        String originalUsername = sysUserPO.getUsername();
        String originalMobile = sysUserPO.getMobile();
        val username = updater.getUsername();
        val mobile = updater.getMobile();

        // 如果设置了用户名 检测用户名是否重复
        if (StringUtils.isNotEmpty(username) && !username.equals(sysUserPO.getUsername())) {
            if (dao.findByUsername(username).isPresent()) {
                throw getExceptionSupplier("用户名已存在", null).get();
            }
        }

        // 如果设置了手机号 检测手机号是否重复
        if (StringUtils.isNotEmpty(mobile) && !mobile.equals(sysUserPO.getMobile())) {
            if (dao.findByMobile(mobile).isPresent()) {
                throw getExceptionSupplier("手机号已存在", null).get();
            }
        }

        // 是否有修改用户名, 修改了需要修改相应的关联的地方
        QBeanUpdaterHelper.copyUpdaterField(sysUserPO, updater);
        if (!StringUtils.equals(originalUsername, username)) {
            if (StringUtils.isEmpty(username)) {
                // 原来有用户名 清空了用户名 要删除用户名登录方式
                userLoginService.findByLoginNameAndLoginNameType(
                                originalUsername,
                                LoginNameType.USERNAME)
                        .forEach(it ->
                                userLoginService.deleteById(it.getId())
                        );
            } else if (StringUtils.isEmpty(originalUsername)) {
                Optional<UserCredential> userCredentialOptional = userCredentialService.findUserDefault(sysUserPO.getId());
                // 原来没有用户名 设置了用户名
                if (userCredentialOptional.isPresent()) {
                    UserLoginCreator.Builder userLoginBuilder = UserLoginCreator.builder()
                            .withUserId(sysUserPO.getId())
                            .withLoginName(sysUserPO.getUsername())
                            .withLoginNameType(LoginNameType.USERNAME)
                            .withLoginCheckType(LoginCheckType.ADMIN_CREDENTIAL)
                            .withLoginCheckId(userCredentialOptional.get().getId());
                    userLoginService.save(userLoginBuilder.build());
                }
            } else {
                // 修改登录方式中的用户名
                userLoginService.findByLoginNameAndLoginNameType(
                                originalUsername,
                                LoginNameType.USERNAME)
                        .forEach(it ->
                                userLoginService.update(
                                        UserLoginUpdater.builder(it.getId()).withLoginName(username).build())
                        );
            }
        }

        // 是否有修改手机号，修改了已验证的手机号时需要去除验证标记并删除手机号登录方式
        if (!StringUtils.equals(originalMobile, mobile)) {
            if (StringUtils.isEmpty(mobile)) {
                // 原来有手机号 清空了手机号 要删除手机号登录方式
                userLoginService.findByLoginNameAndLoginNameType(
                                originalMobile,
                                LoginNameType.PHONE_NUM)
                        .forEach(it ->
                                userLoginService.deleteById(it.getId())
                        );
            } else if (StringUtils.isEmpty(originalMobile)) {
                Optional<UserCredential> userCredentialOptional = userCredentialService.findUserDefault(sysUserPO.getId());
                // 原来没有手机号 设置了手机号
                if (userCredentialOptional.isPresent()) {
                    UserLoginCreator.Builder userLoginBuilder = UserLoginCreator.builder()
                            .withUserId(sysUserPO.getId())
                            .withLoginName(sysUserPO.getMobile())
                            .withLoginNameType(LoginNameType.PHONE_NUM)
                            .withLoginCheckType(LoginCheckType.ADMIN_CREDENTIAL)
                            .withLoginCheckId(userCredentialOptional.get().getId());
                    userLoginService.save(userLoginBuilder.build());
                }
                {
                    UserLoginCreator.Builder userLoginBuilder = UserLoginCreator.builder()
                            .withUserId(sysUserPO.getId())
                            .withLoginName(sysUserPO.getMobile())
                            .withLoginNameType(LoginNameType.PHONE_NUM)
                            .withLoginCheckType(LoginCheckType.ADMIN_VERIFY_CODE)
                            .withLoginCheckId(0L);
                    userLoginService.save(userLoginBuilder.build());
                }
            } else {
                // 修改登录方式中的手机号
                userLoginService.findByLoginNameAndLoginNameType(
                                originalMobile,
                                LoginNameType.PHONE_NUM)
                        .forEach(it ->
                                userLoginService.update(
                                        UserLoginUpdater.builder(it.getId()).withLoginName(mobile).build())
                        );
            }
        }

        userProfileService.update(UserProfileUpdater.builder(sysUserPO.getId())
                .withAgenciesId(agenciesId)
                .build());

        return convertQBean(sysUserPO);
    }

    @Override
    public QResponse<SysUser> findAll(QRequest request, QPage requestPage) {
        return convertQResponse(findAllInternal(request, requestPage));
    }

    private Page<SysUserPO> findAllInternal(QRequest request, QPage requestPage) {
        return dao.findAll(convertSpecification(request), convertPageable(requestPage));
    }

    @Override
    @Transactional
    public void modifyPassword(Long userId, String originalPassword, String newPassword) throws SysUserException {
        SysUserPO sysUserPO = getEntityWithNullCheckForUpdate(userId, dao);

        // TODO: 需要同时修改用户关联的各个表数据

//        String original = passwordHelper.getCredential(originalPassword, sysUserPO.getSalt());
//        if (!original.equals(sysUserPO.getCredential())) {
//            throw new SysUserException("旧密码不匹配");
//        }

        resetPassword(userId, newPassword);
        if (sysUserPO.getLoginOperation() == AdminLoginOperation.MODIFY_PASSWORD) {
            // 清除用户登录必须修改密码的标记
            sysUserPO.setLoginOperation(AdminLoginOperation.DEFAULT);
        }
    }

    @Override
    @Transactional
    public void resetPassword(Long userId, String newPassword) throws SysUserException {
        String salt = passwordHelper.getSalt();
        String credential = passwordHelper.getCredential(newPassword, salt);

        UserCredential userCredential = userCredentialService.getByUserIdEnsure(userId);
        UserCredentialUpdater updater = UserCredentialUpdater.builder(userCredential.getId())
                .withCredential(credential)
                .withSalt(salt)
                .build();

        userCredentialService.update(updater);

    }

    @Override
    @Transactional
    public void enable(Long userId) throws SysUserException {
        SysUserPO sysUserPO = getEntityWithNullCheckForUpdate(userId, dao);

        if (sysUserPO.getStatus() != EnableStatus.DISABLED) {
            throw new SysUserException("当前用户不是禁用状态");
        }

        sysUserPO.setStatus(EnableStatus.ENABLED);
    }

    @Override
    @Transactional
    public void disable(Long userId) throws SysUserException {
        SysUserPO sysUserPO = getEntityWithNullCheckForUpdate(userId, dao);

        if (sysUserPO.getStatus() != EnableStatus.DISABLED) {
            throw new SysUserException("当前用户不是启用状态");
        }

        sysUserPO.setStatus(EnableStatus.DISABLED);
    }

    @Override
    @Transactional
    public void deleteById(Long userId) throws SysUserException {
        SysUserPO sysUserPO = getEntityWithNullCheckForUpdate(userId, dao);

        sysUserPO.setDeleted(System.currentTimeMillis());

        // 需要删除各个相关表的数据
        userProfileService.deleteById(userId);
    }

    @Override
    @Transactional
    public void deleteDisabled(Long userId) throws SysUserException {
        SysUserPO sysUserPO = getEntityWithNullCheckForUpdate(userId, dao);

        if (sysUserPO.getStatus() != EnableStatus.DISABLED) {
            throw new SysUserException("当前用户不是禁用状态");
        }

        deleteById(userId);
    }

    @Override
    @Transactional
    public void enableAndAssignRole(Long userId, String roleCode) throws SysUserException {
        SysUserPO sysUserPO = getEntityWithNullCheckForUpdate(userId, dao);

        if (sysUserPO.getStatus() != EnableStatus.DISABLED) {
            throw new SysUserException("当前用户不是禁用状态");
        }

        Optional<RolePO> rolePOOptional = roleDAO.findByCode(roleCode);
        if (rolePOOptional.isEmpty()) {
            throw new SysUserException("要分配的角色不存在");
        } else {
            RolePO rolePO = rolePOOptional.get();

            SysUserRolePO sysUserRolePO = new SysUserRolePO();
            sysUserRolePO.setRoleId(rolePO.getId());
            sysUserRolePO.setSysUserId(userId);
            sysUserRoleDAO.save(sysUserRolePO);

            sysUserPO.setStatus(EnableStatus.ENABLED);
        }
    }

    @Override
    public List<SysUser> findByIdCollection(Collection<Long> idCollection) {
        return dao.findByIdInOrderByIdAsc(idCollection)
                .stream()
                .map(this::convertQBean)
                .collect(Collectors.toList());
    }

    @Override
    protected Supplier<? extends RuntimeException> getExceptionSupplier(String message,
                                                                        Throwable cause) {
        return () -> new SysUserException(message, cause);
    }

    @Lazy
    @Autowired
    public void setUserProfileService(UserProfileService userProfileService) {
        this.userProfileService = userProfileService;
    }
}
